// retCode project httplib.go
package httplib

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net"
	"net/http"
	"os"
	"time"
)

type HttpClient struct {
	client *http.Client
}

func NewHttpClient() *HttpClient {

	client := &http.Client{
		Transport: &http.Transport{
			DialContext: (&net.Dialer{
				Timeout:   5 * time.Second,
				KeepAlive: 30 * time.Second,
			}).DialContext,
			ResponseHeaderTimeout: time.Second * 5,
			MaxConnsPerHost:       10,
			MaxIdleConnsPerHost:   10,
			MaxIdleConns:          100,
			DisableKeepAlives:     false,
		},
	}

	handle := HttpClient{
		client: client,
	}
	return &handle
}

func (this *HttpClient) GetClient() *http.Client {
	return this.client
}

func (this *HttpClient) Post(url string, body []byte) ([]byte, error) {

	return this.send("POST", url, body)
}

func (this *HttpClient) PostWithHeader(url string, headers map[string]string, body []byte) ([]byte, error) {
	return this._send("POST", url, headers, body)
}

func (this *HttpClient) Get(url string) ([]byte, error) {

	return this.sendGet("GET", url)
}

func (this *HttpClient) GetWithHeader(url string, headers map[string]string) ([]byte, error) {
	return this._sendGet("GET", url, headers)
}

func (this *HttpClient) GetRet(url string) ([]byte, error) {
	return this.GetRetWithHeader(url, nil)
}

func (this *HttpClient) GetRetWithHeader(url string, headers map[string]string) ([]byte, error) {

	req, err := http.NewRequest("GET", url, nil)
	if nil != err {

		return nil, err
	}

	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json;charset=utf-8")
	if headers != nil {
		for k, v := range headers {
			req.Header.Add(k, v)
		}
	}
	resp, err := this.client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	if 200 != resp.StatusCode {
		return nil, errors.New("return status code not 200")
	}

	resbody, err := ioutil.ReadAll(resp.Body)
	return resbody, err

}

func (this *HttpClient) sendGet(method, url string) ([]byte, error) {
	return this._sendGet(method, url, nil)
}

func (this *HttpClient) _sendGet(method, url string, headers map[string]string) ([]byte, error) {

	req, err := http.NewRequest(method, url, nil)
	if nil != err {

		return nil, err
	}

	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json;charset=utf-8")
	if headers != nil {
		for k, v := range headers {
			req.Header.Add(k, v)
		}
	}
	resp, err := this.client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	if 200 != resp.StatusCode {
		return nil, errors.New("return status code not 200")
	}

	resbody, err := ioutil.ReadAll(resp.Body)
	return resbody, err
}

func (this *HttpClient) reqGet(method, url string) ([]byte, error) {
	return this._reqGet(method, url, nil)
}

func (this *HttpClient) _reqGet(method, url string, headers map[string]string) ([]byte, error) {

	req, err := http.NewRequest(method, url, nil)
	if nil != err {

		return nil, err
	}

	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json;charset=utf-8")
	if headers != nil {
		for k, v := range headers {
			req.Header.Add(k, v)
		}
	}
	resp, err := this.client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	if 200 != resp.StatusCode {
		return nil, errors.New("return status code not 200")
	}

	resbody, err := ioutil.ReadAll(resp.Body)
	return resbody, err
}

func (this *HttpClient) send(method, url string, body []byte) ([]byte, error) {
	return this._send(method, url, nil, body)
}

func (this *HttpClient) _send(method, url string, headers map[string]string, body []byte) ([]byte, error) {

	reqBody := bytes.NewBuffer(body)
	req, err := http.NewRequest(method, url, reqBody)

	if nil != err {

		return nil, err
	}

	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json;charset=utf-8")
	req.Header.Set("Connection", "keep-alive")

	len := fmt.Sprintf("%d", len(body))
	req.Header.Set("Content-Length", len)

	if headers != nil {
		for k, v := range headers {
			req.Header.Add(k, v)
		}
	}
	resp, err := this.client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()
	resbody, err := ioutil.ReadAll(resp.Body)

	if 200 != resp.StatusCode {
		errStr := fmt.Sprintf("return status code:%v", resp.StatusCode)
		return nil, errors.New(errStr)
	}

	return resbody, err
}

func (this *HttpClient) UploadFile(url, filename, filePath string, headers, params map[string]string) ([]byte, error) {
	bodyBuf := &bytes.Buffer{}
	bodyWriter := multipart.NewWriter(bodyBuf)

	//关键的一步操作
	fileWriter, err := bodyWriter.CreateFormFile("file", filename)

	if err != nil {
		fmt.Println("error writing to buffer")
		return nil, err
	}

	//打开文件句柄操作
	fh, err := os.Open(filePath)
	if err != nil {
		fmt.Println("error opening file")
		return nil, err
	}

	//iocopy
	_, err = io.Copy(fileWriter, fh)
	if err != nil {
		return nil, err
	}

	if params != nil {
		for k, v := range params {
			_ = bodyWriter.WriteField(k, v)
		}
	}
	_ = bodyWriter.Close()
	defer fh.Close()

	contentType := bodyWriter.FormDataContentType()

	req, err := http.NewRequest("POST", url, bodyBuf)
	if headers != nil {
		for k, v := range headers {
			req.Header.Add(k, v)
		}
	}
	//这里的Content-Type值就是上面contentType的值
	req.Header.Set("Content-Type", contentType)
	resp, err := this.client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	if 200 != resp.StatusCode {
		return nil, errors.New("return status code not 200")
	}

	resbody, err := ioutil.ReadAll(resp.Body)
	return resbody, err
}
